package com.scs.application.modules.wm.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.consts.SerialKeys;
import com.scs.application.core.entity.DataEntity;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.flow.FlowContext;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.BeanUtils;
import com.scs.application.core.utils.DateUtils;
import com.scs.application.core.utils.StringUtils;
import com.scs.application.core.utils.UserUtils;
import com.scs.application.modules.base.entity.Dept;
import com.scs.application.modules.base.entity.Matr;
import com.scs.application.modules.base.entity.Supplier;
import com.scs.application.modules.base.entity.Warehouse;
import com.scs.application.modules.base.mapper.DeptMapper;
import com.scs.application.modules.base.mapper.MatrMapper;
import com.scs.application.modules.base.mapper.SupplierMapper;
import com.scs.application.modules.base.mapper.WarehouseMapper;
import com.scs.application.modules.dept.entity.OprtApply;
import com.scs.application.modules.dept.service.OprtApplyService;
import com.scs.application.modules.purchase.entity.Order;
import com.scs.application.modules.purchase.service.OrderService;
import com.scs.application.modules.wm.dto.WorkOrderKey;
import com.scs.application.modules.wm.entity.*;
import com.scs.application.modules.wm.enums.WorkOrderType;
import com.scs.application.modules.wm.mapper.OprtCheckMapper;
import com.scs.application.modules.wm.mapper.TransferItemMapper;
import com.scs.application.modules.wm.mapper.WorkOrderItemMapper;
import com.scs.application.modules.wm.mapper.WorkOrderMapper;
import com.scs.application.modules.wm.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * <p>
 * </p>
 */
@Slf4j
@Service
public class OprtCheckServiceImpl extends BaseServiceImpl<OprtCheckMapper, OprtCheck> implements OprtCheckService {
    @Resource
    private OprtCheckItemService oprtCheckItemService;

    @Resource
    private MatrMapper matrMapper;

    @Resource
    private SupplierMapper supplierMapper;

    @Resource
    private WorkOrderMapper workOrderMapper;

    @Resource
    private DeptMapper deptMapper;

    @Resource
    private WarehouseMapper warehouseMapper;

    @Resource
    private StockService stockService;

    @Resource
    private StockLockService stockLockService;

    @Resource
    private WorkOrderItemMapper workOrderItemMapper;

    @Resource
    private WorkOrderService workOrderService;

    @Resource
    @Lazy
    private WorkOrderItemService workOrderItemService;

    @Resource
    @Lazy
    private TransferService transferService;

    @Resource
    @Lazy
    private TransferItemService transferItemService;

    @Resource
    private TransferItemMapper transferItemMapper;

    @Resource
    private OutOrderService outOrderService;

    @Resource
    private InOrderService inOrderService;

    @Resource
    private OprtCheckResultService oprtCheckResultService;

    @Resource
    private OrderService orderService;

    @Resource
    private OprtApplyService oprtApplyService;



    @Override
    public boolean saveOrUpdate(OprtCheck entity) {
        if (StringUtils.isBlank(entity.getCheckKey())) {
            entity.setCheckKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.CHECK_KEY));
        }
        return super.saveOrUpdate(entity);
    }

    @Override
    protected void beforeHandleFlowChange(FlowContext flowContext, OprtCheck newEntity) {
        super.beforeHandleFlowChange(flowContext, newEntity);
        long num = this.list(Wrappers.<OprtCheck>query().eq("prepare_id", newEntity.getPrepareId()).eq("flow_status", "end")).size();
        if (num > 0) {
            throw new BusinessException("该备货单已被清点并审核通过，请核查");
        }
    }

    @Override
    protected void afterHandleFlowChange(FlowContext flowContext, OprtCheck newEntity) {
        super.afterHandleFlowChange(flowContext, newEntity);
        boolean endNode = flowContext.isEndNode();
        if (!endNode) {
            if (flowContext.getButtonKey().equals("submit")) { // 开始清点
//                oprtCheckItemService.addCheckItem(newEntity.getId(),newEntity.getPrepareId());
            }else if (flowContext.getButtonKey().equals("back")) { // 取消清点
//                oprtCheckItemService.remove(Wrappers.<OprtCheckItem>query().eq("check_id",newEntity.getId()));
            }
            return;
        }

        // 清点完成
        handleDtl(flowContext, newEntity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeCascadeByIds(Collection<? extends Serializable> idList) {
        for (Serializable id : idList) {
            // 调用子表 查询子表信息，如果没有跳过，有的删除
            List<OprtCheckItem> checkItems = oprtCheckItemService.list(Wrappers.<OprtCheckItem>query().eq("check_id", id));
            if (checkItems == null || checkItems.size() < 1) continue;
            oprtCheckItemService.removeCascadeByIds(checkItems.stream().map(o -> o.getId()).collect(Collectors.toList()));
        }
        return super.removeCascadeByIds(idList);
    }

    /**
     * @Description：消耗、退货、退洁、退污操作
     * <pre>
     *  消耗  将产生可消耗库存用于【科室扫码消耗】操作
     *  退货  将指定数量的商品进行【供应商退货】操作
     *  退洁  将指定数量的商品调拨到【科室仓库上级仓库】进行清洁操作
     *  退污  将指定数量的商品调拨到【消毒仓库】进行污染处理
     * </pre>
    */
    private void handleDtl(FlowContext flowContext, OprtCheck newEntity) {
        OprtCheck entity = flowContext.getBusEntity();
        List<OprtCheckItem> oprtCheckItems = oprtCheckItemService.list(Wrappers.<OprtCheckItem>query().eq("check_id", entity.getId()));
        if (oprtCheckItems.isEmpty()) {
            throw new BusinessException("该清点单没有添加明细，无法结束清点");
        }
        oprtCheckItems.stream().filter(s ->
                    s.getPatientQty() + s.getSpQty() != s.getSkuQty()
            )
            .findFirst()
            .ifPresent( item -> {
                BusinessException.throwError("商品【%s】，消耗、退货合计数量不等于备货数量，请检查",item.getMatrName());
        });

        Dept dept = deptMapper.selectById(entity.getDeptId());
        if (StringUtils.isBlank(dept.getWarehouseId())) {
            throw new BusinessException("该科室未配置默认仓库,单据生成失败");
        }

        Warehouse warehouseDept = warehouseMapper.selectById(dept.getWarehouseId());
        BusinessException.throwErrorIf(warehouseDept == null,"科室仓库未找到");

        WorkOrder workOrder = workOrderService.getById(newEntity.getWorkOrderId());
        BusinessException.throwErrorIf(ObjectUtil.isNull(workOrder), "跟台入库单未找到，请联系运营");
        List<WorkOrderItem> workOrderItemList = workOrderItemMapper.selectList(Wrappers.<WorkOrderItem>query().eq("work_order_id",workOrder.getId()));
        BusinessException.throwErrorIf(ObjectUtil.length(workOrderItemList) == 0, "跟台入库单未找到可用明细，请联系运营");

        // 产生需要操作的库存  .stream().collect(Collectors.toMap(Stock::getId, item -> item));
        Map<String,List<Stock>> stockByLineId = workOrderService.addStockBatchForPack(workOrder,workOrderItemList).stream().collect(Collectors.groupingBy(Stock::getLineId));
        BusinessException.throwErrorIf(stockByLineId == null || stockByLineId.keySet().size() != oprtCheckItems.size(),"库存缺失，请联系运营人员");

        List<String> msgList = new ArrayList<>();
        // 退洁
//        msgList.add(returnCleanQty(entity,oprtCheckItems,dept, stockMapById));

        // 退污
//        msgList.add(returnPolluteQty(entity,oprtCheckItems,dept,stockMapById));

        // 供应商退货
        spHandle(entity,workOrder, dept,oprtCheckItems,stockByLineId);

        // 科室消耗
        patientHandle(entity,workOrder, dept,oprtCheckItems,stockByLineId,warehouseDept);


//       List<StockLock> stockLockList =  stockLockService.list(Wrappers.<StockLock>query().in("stock_id", oprtCheckItems.stream().map(OprtCheckItem::getStockId).collect(Collectors.toList())));
//       if (ObjectUtil.length(stockLockList) > 0) {
//           stockLockService.unlockBatch(stockLockList.stream().map(StockLock::getUc).collect(Collectors.toList()));
//       }

        if (msgList.size() > 0) {
            flowContext.setHandledMsg("清点完成," + StringUtils.join(msgList,","));
        }
    }


    /***
     * @param oprtCheck
     * @param workOrder
     * @param dept
     * @param oprtCheckItems
     * @param stockByLineId
     * @Author: Tai
     * @date: 2024/6/4
     * @Description:  产生供应商退货单
     */
    private void spHandle(OprtCheck oprtCheck,WorkOrder workOrder,Dept dept,List<OprtCheckItem> oprtCheckItems,Map<String,List<Stock>> stockByLineId) {
        List<OprtCheckItem> oprtCheckItemsSub = oprtCheckItems.stream().filter(item -> item.getSpQty() > 0).collect(Collectors.toList());
        if (ObjectUtil.length(oprtCheckItemsSub) == 0) {
            return;
        }
        WorkOrder wo = new WorkOrder();
        wo.setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.OUT_ORDER_KEY))
                .setDirection(GlobalConsts.ORDER_DIRECTION_OUT)
                .setBusDate(new Date()).setBusUser(oprtCheck.getFlowModifier()).setBusDate(new Date())
                .setBusType(WorkOrderType.OUT_SP)
                .setFlagConsignment(workOrder.getFlagConsignment()).setFlagOmoc(workOrder.getFlagOmoc())
                .setRefId(oprtCheck.getId()).setRefKey(oprtCheck.getCheckKey()).setRefTable("wm_oprt_check")
                .setSupplierId(workOrder.getSupplierId()).setSupplierName(workOrder.getSupplierName())
                .setWarehouseId(workOrder.getId()).setWarehouseName(workOrder.getWarehouseName())
                .setDeliveryman(oprtCheck.getFlowModifier())
                .setAmount(0d)
                .setDeptId(oprtCheck.getDeptId()).setDeptName(dept.getName())
                .setRemark(String.format("由跟台手术清点单%s退货自动转换而来", oprtCheck.getCheckKey()))
                .setFlowStatus(GlobalConsts.FLOW_END_VALUE)
                .setFlowModifier(oprtCheck.getFlowModifier())
                .setFlowModified(oprtCheck.getFlowModified()).setId(IdWorker.getIdStr())
        ;
        List<OprtCheckResult> checkResultList = new ArrayList<>();
        List<WorkOrderItem> workOrderItemList = new ArrayList<>();
        List<Stock> stockSub;
        List<Stock> stockSubAll = new ArrayList<>();
        List<String> snList = new ArrayList<>();
        for (OprtCheckItem item : oprtCheckItemsSub) {
            stockSub = stockByLineId.get(item.getWorkOrderItemId()).subList(0,item.getSpQty().intValue());
            String stockId = item.getStockId();
            BusinessException.throwErrorIf(ObjectUtil.length(stockSub) == 0, "库存缺失，请检查");

            for(Stock stock  : stockSub) {
                WorkOrderItem orderItem = new WorkOrderItem();
                String[] ignoreFields = Arrays.asList(DataEntity.class.getDeclaredFields())
                        .stream().map(field -> field.getName()).collect(Collectors.toList()).toArray(new String[]{});
                BeanUtils.copyProperties(stock, orderItem, ignoreFields);
                orderItem.setStockId(stockId);//.setSkuQty(item.getSpQty()).setAmount(item.getSkuPrice() * item.getSpQty()).setPackageQty(item.getSpQty() / (item.getSkuQty() / item.getPackageQty()));
                orderItem.setWorkOrderId(wo.getId()).setRemark(String.format("由跟台手术清点单 %s 退货自动转换而来", oprtCheck.getCheckKey()));

                orderItem.setRefBusItemId(item.getId());
                OprtCheckResult checkResult = new OprtCheckResult();
                checkResult.setCheckId(item.getCheckId()).setCheckItemId(item.getId())
                        .setResultName("供应商退货").setSn(stock.getSn()).setBusKey(wo.getBusKey()).setSnNew(stock.getSn())
                        .setRemark("已退货");
                ;
                checkResultList.add(checkResult);
                workOrderItemList.add(orderItem);
                snList.add(stock.getSn());
                stockSubAll.add(stock);
            }
            stockByLineId.get(item.getWorkOrderItemId()).removeIf(stockTmp -> snList.contains(stockTmp.getSn()));
        }
        workOrderItemService.saveBatch(workOrderItemList);
        oprtCheckResultService.saveBatch(checkResultList);
        wo.setAmount(workOrderItemList.stream().mapToDouble(WorkOrderItem::getAmount).sum());
        workOrderMapper.insert(wo);
        workOrderService.resetStockBatch(stockSubAll);
    }

    //科室消耗
    private  void patientHandle(OprtCheck oprtCheck,WorkOrder workOrder,Dept dept,List<OprtCheckItem> oprtCheckItems,Map<String,List<Stock>> stockByLineId,Warehouse warehouseDept){
        List<OprtCheckItem> oprtCheckItemsSub = oprtCheckItems.stream().filter(item -> item.getPatientQty() > 0).collect(Collectors.toList());
        if (ObjectUtil.length(oprtCheckItemsSub) == 0) {
            return;
        }
        List<OprtCheckResult> checkResultList = new ArrayList<>();
        List<Stock> stockSub;
        List<String> snList = new ArrayList<>();
        double amount = 0;
        String transferKey = serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.WM_TRANSFER_BUS_KEY);
        for (OprtCheckItem item : oprtCheckItemsSub) {
            stockSub = stockByLineId.get(item.getWorkOrderItemId());
            BusinessException.throwErrorIf(ObjectUtil.length(stockSub) == 0, "库存缺失，请检查");
            for(Stock stock  : stockSub) {
                OprtCheckResult checkResult = new OprtCheckResult();
                checkResult.setCheckId(item.getCheckId()).setCheckItemId(item.getId())
                        .setResultName("消耗").setSn(item.getSn()).setSnNew(stock.getSn()).setBusKey(transferKey)
                        .setRemark(StrUtil.format("可消耗，已调拨到:{}",warehouseDept.getName()));
                checkResultList.add(checkResult);
                snList.add(stock.getSn());
                amount+=stock.getAmount();
            }
            stockByLineId.get(item.getWorkOrderItemId()).removeIf(stockTmp -> snList.contains(stockTmp.getSn()));
        }
        oprtCheckResultService.saveBatch(checkResultList);

        //将消耗库存调拨到对应二级库
        Transfer transfer = new Transfer();
        transfer.setFromWarehouseId(workOrder.getWarehouseId()).setFromWarehouseName(workOrder.getWarehouseName())
                        .setToWarehouseId(warehouseDept.getId()).setToWarehouseName(warehouseDept.getName())
                        .setBusUser(UserUtils.currentUser().getName()).setBusDate(DateUtils.now())
                        .setType(WorkOrderType.OUT_SHIFT).setSourceType("manual")
                .setFlowStatus(GlobalConsts.FLOW_END_VALUE).setFlowModified(LocalDateTime.now()).setFlowModifier(UserUtils.currentUser().getName());
        transfer.setSettlementType(GlobalConsts.SettlementType.usedSettlement).setAmount(amount).setBusKey(transferKey);
        transfer.setRemark(String.format("由跟台清点单%s产生,调拨转换", oprtCheck.getCheckKey()));

        transferService.saveOrUpdate(transfer);
        transferItemService.addTransferItems(transfer.getId(),snList, null);
        transferService.autoOutIn(transfer, true);
    }

    //科室消耗
    private  String patientQty(OprtCheck entity,List<OprtCheckItem> oprtCheckItems,Map<String,Stock> stockMapById){
        List<OprtCheckItem> skuCheckItem = oprtCheckItems.stream().filter(s -> s.getPatientQty() > 0).filter(s -> "0".equals(s.getMatrType())).collect(Collectors.toList());
        if (ObjectUtil.length(skuCheckItem) == 0 ) {
            return "";
        }
        AtomicInteger num = new AtomicInteger();
        List<OprtCheckResult> checkResultList = new ArrayList<>();
        skuCheckItem.stream().forEach(item -> {
            Stock stock = stockMapById.get(item.getStockId());
            stock.setSkuQty(1d);
            stock.setPackageQty(1d);
            stock.setFlagConsignment(true);
            stock.setFlagOmoc(true);
            stock.setFlagUniqueCode(true);
            stock.setInTime(new Date());
            stock.setRemark(String.format("由跟台清点单%s产生,消耗转换", entity.getCheckKey()));
            for (int i = 0; i < item.getPatientQty(); i++) {
                stock.setId(null);
                stock.setSn(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.WM_STOCK_PROCESS_SN));
                stockService.save(stock);
                num.getAndIncrement();
                OprtCheckResult checkResult = new OprtCheckResult();
                checkResult.setCheckId(item.getCheckId()).setCheckItemId(item.getId())
                        .setResultName("消耗").setSn(item.getSn()).setSnNew(stock.getSn())
                        .setRemark(StrUtil.format("已产生可消耗库存，用于科室消耗"));
                checkResultList.add(checkResult);
            }
        });
        oprtCheckResultService.saveBatch(checkResultList);
        return StrUtil.format("生成库存记录{}",num);
    }

    //供应商退货
    private  String spQty(OprtCheck entity,List<OprtCheckItem> oprtCheckItems,Map<String,Stock> stockMapById){
        //供应商退货
        List<OprtCheckItem> spCheckItem = oprtCheckItems.stream().filter(s -> !s.getMatrType().equals("1") && s.getSpQty() > 0).collect(Collectors.toList());
        if (ObjectUtil.length(spCheckItem) == 0 ) {
            return "";
        }
        // 生成出库单
        List<WorkOrder> wos = convertUseToOrders(entity, spCheckItem);
        // 修改记账单信息 手术单号等
        //       过账
        List<String> workIds = wos.stream().map(WorkOrder::getId).collect(Collectors.toList());
        workOrderService.postBatch(workIds);

        return StrUtil.format("产生供应商退货单{}",wos.size());
    }

    //退洁
    private  String returnCleanQty(OprtCheck entity,List<OprtCheckItem> oprtCheckItems,Dept dept,Map<String,Stock> stockMapById){
        List<OprtCheckItem> returnCleanCheckItems = oprtCheckItems.stream().filter(s -> s.getReturnCleanQty() > 0).collect(Collectors.toList());
        if (ObjectUtil.length(returnCleanCheckItems) == 0 ) {
            return "";
        }
        Warehouse warehouse = warehouseMapper.selectById(dept.getWarehouseId());
        if (StringUtils.isBlank(warehouse.getApplyWarehouseId())) {
            throw new BusinessException("该科室未配置上级仓库,单据生成失败");
        }
        Transfer transfer = new Transfer();
        transfer.setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.WM_TRANSFER_BUS_KEY))
                .setBusDate(new Date()).setBusUser(UserUtils.currentUser().getName())
                .setFromWarehouseId(dept.getWarehouseId()).setFromWarehouseName(dept.getWarehouseName())
                .setToWarehouseId(warehouse.getApplyWarehouseId()).setToWarehouseName(warehouse.getApplyWarehouseName())
                .setRemark(String.format("由跟台清点单%s产生,退洁转换", entity.getCheckKey()))
                .setFlowModifier(UserUtils.currentUser().getName()).setFlowModified(LocalDateTime.now())
        ;
        transfer.setFlowStatus(GlobalConsts.FLOW_END_VALUE);
        transfer.setType(WorkOrderType.OUT_SHIFT);
        transferService.saveOrUpdate(transfer);
        List<OprtCheckResult> checkResultList = new ArrayList<>();
        returnCleanCheckItems.stream().forEach(item -> {
            TransferItem transferItem = new TransferItem();
            Stock stock = stockMapById.get(item.getStockId());
            if (stock == null) {
                throw new BusinessException("未能按照提供的StockId检索到库存记录，stockId：" + item.getStockId());
            }
            BeanUtils.copyProperties(stock, transferItem);
            transferItem.setStockId(stock.getId())
                    .setTransferId(transfer.getId())
                    .setSkuQty(item.getReturnCleanQty())
                    .setAmount(item.getSkuPrice() * item.getReturnCleanQty())
                    .setPackageQty(item.getReturnCleanQty() / (item.getSkuQty() / item.getPackageQty()))
                    .setId(null);
            transferItemMapper.insert(transferItem);
            OprtCheckResult checkResult = new OprtCheckResult();
            checkResult.setCheckId(item.getCheckId()).setCheckItemId(item.getId())
                .setResultName("退洁").setSn(stock.getSn())
                .setBusKey(transfer.getBusKey())
                .setRemark(StrUtil.format("已产生调拨单，来源仓库: {}，目标仓库: {}",transfer.getFromWarehouseName(),transfer.getToWarehouseName()));
            checkResultList.add(checkResult);
        });
        List<WorkOrder> outWorkOrderList = outOrderService.createWorkOrderByTransfer(transfer);
        if (outWorkOrderList == null || outWorkOrderList.size() == 0) {
            throw new BusinessException("转换出库单失败");
        }
        outWorkOrderList.forEach(item -> {
            WorkOrder inWorkOrder = inOrderService.createWorkOrderByOut(item, transfer);
            workOrderService.post(inWorkOrder.getId(), item.getId(),false);
        });
        log.info("调拨共生成{}个入库单", outWorkOrderList.size());
        oprtCheckResultService.saveBatch(checkResultList);
        return StrUtil.format("产生退洁单1");
    }

    //退污
    private  String returnPolluteQty(OprtCheck entity,List<OprtCheckItem> oprtCheckItems,Dept dept,Map<String,Stock> stockMapById){
        //退污
        List<OprtCheckItem> returnPolluteCheckItems = oprtCheckItems.stream().filter(s -> s.getReturnPolluteQty() > 0).collect(Collectors.toList());
        if (ObjectUtil.length(returnPolluteCheckItems) == 0 ) {
            return "";
        }
        Transfer transfer = new Transfer();
        transfer.setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.WM_TRANSFER_BUS_KEY))
                .setBusDate(new Date()).setBusUser(UserUtils.currentUser().getName())
                .setFromWarehouseId(dept.getWarehouseId()).setFromWarehouseName(dept.getWarehouseName())
                .setToWarehouseId(entity.getWarehouseId()).setToWarehouseName(entity.getWarehouseName())
                .setRemark(String.format("由跟台清点单%s产生,退污转换", entity.getCheckKey()))
                .setFlowModifier(UserUtils.currentUser().getName()).setFlowModified(LocalDateTime.now());
        transfer.setType(WorkOrderType.OUT_SHIFT);
        transfer.setFlowStatus(GlobalConsts.FLOW_END_VALUE);
        transferService.saveOrUpdate(transfer);
        List<OprtCheckResult> checkResultList = new ArrayList<>();
        returnPolluteCheckItems.stream().forEach(item -> {
            TransferItem transferItem = new TransferItem();
            Stock stock = stockMapById.get(item.getStockId());
            if (stock == null) {
                throw new BusinessException("未能按照提供的StockId检索到库存记录，stockId：" + item.getStockId());
            }
            BeanUtils.copyProperties(stock, transferItem);
            transferItem.setStockId(stock.getId())
                    .setTransferId(transfer.getId())
                    .setSkuQty(item.getReturnPolluteQty())
                    .setAmount(item.getSkuPrice() * item.getReturnPolluteQty())
                    .setPackageQty(item.getReturnPolluteQty() / (item.getSkuQty() / item.getPackageQty()))
                    .setId(null);
            transferItemMapper.insert(transferItem);
            OprtCheckResult checkResult = new OprtCheckResult();
            checkResult.setCheckId(item.getCheckId()).setCheckItemId(item.getId())
                    .setResultName("退污").setSn(stock.getSn())
                    .setBusKey(transfer.getBusKey())
                    .setRemark(StrUtil.format("已产生调拨单，来源仓库: {}，目标仓库: {}",transfer.getFromWarehouseName(),transfer.getToWarehouseName()));
            checkResultList.add(checkResult);
        });
        List<WorkOrder> outWorkOrderList = outOrderService.createWorkOrderByTransfer(transfer);
        if (outWorkOrderList == null || outWorkOrderList.size() == 0) {
            throw new BusinessException("转换出库单失败");
        }
        outWorkOrderList.forEach(item -> {
            WorkOrder inWorkOrder = inOrderService.createWorkOrderByOut(item, transfer);
            workOrderService.post(inWorkOrder.getId(), item.getId(),false);
        });
        oprtCheckResultService.saveBatch(checkResultList);
        log.info("调拨共生成{}个入库单", outWorkOrderList.size());
        return StrUtil.format("产生退污单1");
    }

    private List<WorkOrder> convertUseToOrders(OprtCheck oprtCheck, List<OprtCheckItem> oprtCheckItems) {

        List<Matr> matrs = matrMapper.selectBatchIds(oprtCheckItems.stream().map(item -> item.getMatrId()).collect(Collectors.toList()));
        if (matrs.isEmpty()) {
            throw new BusinessException("该手术清点单所对应的耗材在系统中不存在，转换出库单失败");
        }
        // key为耗材ID，value为耗材对象
        Map<String, Matr> matrMap = matrs.stream().collect(Collectors.toMap(matr -> matr.getId(), matr -> matr));

        Map<WorkOrderKey, List<OprtCheckItem>> groupDemandItem = Maps.newHashMap();

        // 依据供应商、耗材购销/代销、一物一码属性进行拆分
        for (OprtCheckItem oprtCheckItem : oprtCheckItems) {
            String matrId = oprtCheckItem.getMatrId();

            Matr matr = matrMap.get(matrId);
            if (matr == null) {
                // 明细中的耗材 在 耗材表中 不存在
                throw new BusinessException("品名：%s，品规：%s 在耗材基础数据中不存在，转换科室配送出库单失败",
                        oprtCheckItem.getMatrName(), oprtCheckItem.getMatrSpec());
            }
            Boolean flagConsignment = matr.getFlagConsignment();
            Boolean flagOmoc = matr.getFlagOmoc();

            WorkOrderKey orderKey = new WorkOrderKey(matr.getSupplierId(), matr.getSupplierName(), flagConsignment, flagOmoc);
            List<OprtCheckItem> items = groupDemandItem.get(orderKey);
            if (items == null) {
                items = Lists.newArrayList();
            }
            items.add(oprtCheckItem);
            groupDemandItem.put(orderKey, items);
        }

        ArrayList<WorkOrder> orders = Lists.newArrayList();
        for (WorkOrderKey orderKey : groupDemandItem.keySet()) {
            WorkOrder order = convertDemandItemsToOrder(orderKey, oprtCheck, groupDemandItem.get(orderKey));
            orders.add(order);
        }
        return orders;
    }

    private WorkOrder convertDemandItemsToOrder(WorkOrderKey orderKey, OprtCheck oprtCheck, List<OprtCheckItem> oprtCheckItem) {

        String supplierId = orderKey.supplierId;
        Supplier supplier = supplierMapper.selectById(supplierId);
        if (supplier == null) {
            log.error("转换出库单失败，清点单号：{}，供应商 {} 不存在", oprtCheck.getCheckKey(), supplierId);
            throw new BusinessException("转换出库单失败");
        }

        // 出库金额求和
        double sum = oprtCheckItem.stream().mapToDouble(o -> o.getSkuPrice() * o.getSpQty()).sum();

        WorkOrder wo = new WorkOrder();

        String name = oprtCheck.getCreator();
        if (UserUtils.currentUser() != null) {
            name = UserUtils.currentUser().getName();
        }
        Dept dept = deptMapper.selectById(oprtCheck.getDeptId());

        wo.setBusKey(serialRuleAdapter.getSerialRuleService().generateCode(SerialKeys.OUT_ORDER_KEY))
                .setDirection(GlobalConsts.ORDER_DIRECTION_OUT)
                .setBusDate(new Date()).setBusUser(name).setBusDate(new Date())
                .setBusType(WorkOrderType.OUT_SP)
                .setFlagConsignment(orderKey.getFlagConsignment()).setFlagOmoc(orderKey.getFlagOmoc())
                .setRefId(oprtCheck.getId()).setRefKey(oprtCheck.getCheckKey()).setRefTable("wm_oprt_check")
                .setSupplierId(supplier.getId()).setSupplierName(supplier.getName())
                .setWarehouseId(dept.getWarehouseId()).setWarehouseName(dept.getWarehouseName())
                .setDeliveryman(name)
                .setAmount(sum)
                .setDeptId(oprtCheck.getDeptId()).setDeptName(dept.getName())
                .setRemark(String.format("由跟台手术清点单%s退货自动转换而来", oprtCheck.getCheckKey()))
                .setFlowStatus(GlobalConsts.FLOW_END_VALUE)
                .setFlowModifier(oprtCheck.getFlowModifier())
                .setFlowModified(oprtCheck.getFlowModified())
        ;
        workOrderMapper.insert(wo);
        List<OprtCheckResult> checkResultList = new ArrayList<>();
        for (OprtCheckItem item : oprtCheckItem) {
            String stockId = item.getStockId();
            Stock stock = stockService.getById(stockId);
            if (stock == null) {
                log.error("库存不存在，SN：{}，RFID：{}，UDI：{}，StockId：{}",
                        item.getSn(), item.getRfid(), item.getUdi(), stockId);
                throw new BusinessException("库存不存在，可能是由于数据错误");
            }
            WorkOrderItem orderItem = new WorkOrderItem();
            String[] ignoreFields = Arrays.asList(DataEntity.class.getDeclaredFields())
                    .stream().map(field -> field.getName()).collect(Collectors.toList()).toArray(new String[]{});
            BeanUtils.copyProperties(stock, orderItem, ignoreFields);
            orderItem.setStockId(stockId).setSkuQty(item.getSpQty()).setAmount(item.getSkuPrice() * item.getSpQty()).setPackageQty(item.getSpQty() / (item.getSkuQty() / item.getPackageQty()));
            orderItem.setWorkOrderId(wo.getId()).setRemark(String.format("由跟台手术清点单 %s 退货自动转换而来", oprtCheck.getCheckKey()));

            orderItem.setRefBusItemId(item.getId());
            workOrderItemMapper.insert(orderItem);
            OprtCheckResult checkResult = new OprtCheckResult();
            checkResult.setCheckId(item.getCheckId()).setCheckItemId(item.getId())
                    .setResultName("供应商退货").setSn(stock.getSn()).setBusKey(wo.getBusKey())
                    .setRemark(StrUtil.format("已产生退货单，来源仓库: {}",wo.getWarehouseName()));
            ;
            checkResultList.add(checkResult);
        }
        oprtCheckResultService.saveBatch(checkResultList);

        return wo;
    }

    /**
     * 转清点单
     *
     * @param workOrder
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public OprtCheck toOprtCheck(WorkOrder workOrder) {
        workOrder = workOrderService.getById(workOrder.getId()); //再请求一遍
        String refTransferId = workOrder.getRefTransferId();
        if (refTransferId != null && !"0".equalsIgnoreCase(refTransferId)) {
            throw new BusinessException(String.format("该订单已经转换过清点单，清点单号为%s，不能重复转换", refTransferId));
        }
        Order order = orderService.getById(workOrder.getPurchaseOrderId());
        BusinessException.throwErrorIf(ObjectUtil.isNull(order), "未找到相关采购订单，无法转换");

        OprtApply oprtApply = oprtApplyService.getById(order.getRefId());
        BusinessException.throwErrorIf(ObjectUtil.isNull(oprtApply), "未找到相关科室备货单，无法转换");
        // 查询送货明细
        List<WorkOrderItem> workOrderItems = workOrderItemMapper.selectList(Wrappers.<WorkOrderItem>query().eq("work_order_id", workOrder.getId()));
        if (workOrderItems.isEmpty()) {
            throw new BusinessException("送货明细为空，转换失败");
        }

        // 清点单
        OprtCheck oprtCheck = new OprtCheck();
        BeanUtils.copyProperties(oprtApply,oprtCheck);
        oprtCheck.setId(null);
        oprtCheck.setPrepareType(oprtApply.getOprtApplyType()).setPrepareId(oprtApply.getId()).setPrepareKey(oprtApply.getOprtApplyKey())
                .setWarehouseId(oprtApply.getWarehouseIdTo())
                .setWarehouseName(oprtApply.getWarehouseNameTo())
                .setCheckDate(DateUtils.now())
        ;
        oprtCheck.setFlowStatus(GlobalConsts.FLOW_START_VALUE).setFlowModified(null).setFlowModified(null);
        oprtCheck.setCheckPersion2(UserUtils.currentUser().getName());
        oprtCheck.setWorkOrderId(workOrder.getId());
        oprtCheck.setWorkOrderKey(workOrder.getBusKey());
        this.saveOrUpdate(oprtCheck);

        workOrderService.update(Wrappers.<WorkOrder>update().set("ref_transfer_id", oprtCheck.getId()).eq("id",workOrder.getId()));

        // 清点明细
        List<OprtCheckItem> itemList = new ArrayList<>();
        OprtCheckItem item;
        for(WorkOrderItem workOrderItem : workOrderItems) {
            item = new OprtCheckItem();
            BeanUtils.copyProperties(workOrderItem,item);
            item.setCheckId(oprtCheck.getId());
            item.setWorkOrderItemId(workOrderItem.getId());
            item.setId(null);
            item.setSupplierId(workOrder.getSupplierId()).setSupplierName(workOrder.getSupplierName());
            itemList.add(item);
        }

        oprtCheckItemService.saveBatch(itemList);

        return oprtCheck;
    }
}
